#include "text.h"

#include "mem.h"

#include <string.h>
#include <assert.h>
#include <stdlib.h>

static void
test_case1()
{
    const char *str = "hello, world";
    Text_T s = Text_put(str);
    assert(strncmp(s.str, str, s.len) == 0);

    int len = strlen(str);
    char *p = malloc(len+1);
    p = Text_get(p, len+1, s);
    assert(strcmp(p, str) == 0);
    free(p); // 需要释放内存

    p = Text_get(NULL, 0, s);
    assert(strcmp(p, str) == 0); // 不需要释放内存

    p = malloc(len+1);
    strcpy(p, str);
    Text_T s_free1 = Text_box(p, len);
    assert(strncmp(s_free1.str, s.str, s_free1.len) == 0);
    free(p); // 不再使用 s_free1 释放内存

    p = malloc(len+1);
    strcpy(p, str);
    Text_T s_free2 = {len, p};
    assert(strncmp(s_free2.str, s.str, s_free2.len) == 0);
    free(p); // 不再使用 s_free2 释放内存

    assert(Text_pos(s, 1) == 0);
    assert(Text_pos(s, len) == len-1);
    assert(Text_pos(s, len+1) == len);
    assert(Text_pos(s, 0) == len);
    assert(Text_pos(s, -1) == len-1);
    assert(Text_pos(s, -len) == 0);

    Text_T s2 = Text_put("hello, world");
    Text_T s3 = Text_put("hello, world2");
    assert(Text_cmp(s, s2) == 0);
    assert(Text_cmp(s, s3) == -1);
    assert(Text_cmp(s3, s) == 1);

    Text_T s4 = Text_put("hf");
    assert(Text_cmp(s, s4) == -1);
    assert(Text_cmp(s4, s) == 1);

    Fmt_register('a', Text_fmt);
    p = Fmt_string("%a", &s);
    assert(strcmp(p, str) == 0);
    FREE(p);
}

static void
test_case2()
{
    const char *str = "hello, world";
    Text_T s = Text_put(str);
    Text_T s2 = Text_sub(s, 1, 0);
    assert(Text_cmp(s, s2) == 0);

    assert(Text_cat(Text_null, s).str == s.str);
    assert(Text_cat(s, Text_null).str == s.str);

    Text_T s3 = Text_cat(s2, s);
    assert(s3.str == s2.str); // 见 Text_cat 优化

    assert(Text_dup(s3, 0).str == Text_null.str);
    assert(Text_dup(s3, 1).str == s3.str);
    
    Text_T s4 = Text_put(str);
    Text_T s5 = Text_dup(s4, 2);
    assert(s5.str == s4.str); // 见 Text_dup 优化

    assert(Text_reverse(Text_null).str == Text_null.str);
    Text_T s6 = Text_put("abcd");
    Text_T s7 = Text_reverse(s6);
    assert(strncmp(s7.str, "dcba", s7.len) == 0);

    Text_T s8 = Text_put("ABCD");
    Text_T s9 = Text_map(s7, &s6, &s8);
    assert(strncmp(s9.str, "DCBA", s9.len) == 0);
}

static void
test_case3()
{
    Text_T s = Text_put("h");
    Text_save_T h = Text_save();

    Text_T s1 = Text_put("e");
    assert(s.str+2 == s1.str); // 假设此时没有调用 alloc 分配新的片

    Text_restore(&h); // 恢复之前的缓冲区
    Text_T s2 = Text_put("e");
    assert(s.str+1 == s2.str);
}

static void
test_case4()
{
    const char *str = "hello, world";
    Text_T s = Text_put(str);
    assert(Text_chr(s, 1, 0, 'z') == 0);
    assert(Text_chr(s, 1, 0, 'l') == 3);

    assert(Text_rchr(s, 1, 0, 'z') == 0);
    assert(Text_rchr(s, 1, 0, 'l') == 11);

    Text_T s2 = Text_put("xyz");
    Text_T s3 = Text_put("xyzl");
    assert(Text_upto(s, 1, 0, s2) == 0);
    assert(Text_upto(s, 1, 0, s3) == 3);

    assert(Text_rupto(s, 1, 0, s2) == 0);
    assert(Text_rupto(s, 1, 0, s3) == 11);

    Text_T s4 = Text_put("HELLO");
    Text_T s5 = Text_put("hello");
    assert(Text_find(s, 1, 0, s4) == 0);
    assert(Text_find(s, 1, 0, s5) == 1);

    assert(Text_rfind(s, 1, 0, s4) == 0);
    assert(Text_rfind(s, 1, 0, s5) == 1);
}

static void
test_case5()
{
    const char *str = "hello, world";
    Text_T s = Text_put(str);
    Text_T s1 = Text_put("h");
    Text_T s2 = Text_put("H");
    Text_T s3 = Text_put("Hh");
    assert(Text_any(s, 1, s1) == 2);
    assert(Text_any(s, 1, s2) == 0);
    assert(Text_any(s, 1, s3) == 2);

    Text_T s4 = Text_put("h-ello");
    Text_T s5 = Text_put("z"); 
    assert(Text_many(s, 1, 0, s4) == 6);
    assert(Text_many(s, 1, 0, s5) == 0);

    Text_T s6 = Text_put("w-orld");
    assert(Text_rmany(s, 1, 0, s6) == 8);
    assert(Text_rmany(s, 1, 0, s5) == 0);

    Text_T s7 = Text_put("hello");
    assert(Text_match(s, 1, 6, s7) == 6);
    assert(Text_match(s, 1, 6, s5) == 0);

    Text_T s8 = Text_put("world");
    assert(Text_rmatch(s, -5, 0, s8) == 8);
    assert(Text_rmatch(s, -5, 0, s5) == 0);
}

static Text_T
basename(char *path, int i, int j, const char *suffix)
{
    Text_T s = Text_put(path);
    Text_T s1 = Text_put(suffix);
    i = Text_rchr(s, i, j, '/');
    j = Text_rmatch(s, i+1, 0, s1);
    return Text_sub(s, i+1, j);
}

static void
test_basename()
{
    Text_T s;
    s = basename("/usr/jenny/main.c", 1, 0, ".c");
    assert(strncmp(s.str, "main", s.len) == 0);

    s = basename("../src/main.c", 1, 0, "");
    assert(strncmp(s.str, "main.c", s.len) == 0);

    s = basename("main.c", 1, 0, ".obj");
    assert(strncmp(s.str, "main.c", s.len) == 0);

    s = basename("examples/wfmain.c", 1, 0, "main.c");
    assert(strncmp(s.str, "wf", s.len) == 0);
}

int main()
{
    test_case1();
    test_case2();
    test_case3();
    test_case4();
    test_case5();
    test_basename();
    return 0;
}
